package com.wulis.seckill.business.service.impl;

import com.wulis.seckill.enums.SecKillInventoryEnum;
import com.wulis.seckill.redis.RedisUtils;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.wulis.seckill.common.util.CommonVariableUtil.LOCK_STOCK;
import static com.wulis.seckill.common.util.CommonVariableUtil.USER_SEC_KILL;

/**
 * redis缓存中库存管理
 */
@Service
@AllArgsConstructor
@Slf4j
public class CacheStockServiceImpl {
    /**
     * redis分布式锁
     */
    private final Redisson redisson;
    /**
     * redisUtils
     */
    private final RedisUtils redisUtils;

    /**
     * 减少redis缓存库存，并记录用户操作
     *
     * @param key
     * @param num
     * @param userId
     * @return
     */
    public String reduceStock(String key, int num, String userId) {
        String code;
        //获取锁
        RLock lock = redisson.getLock(LOCK_STOCK + "_" + key);
        try {
            lock.lock();
            if (!redisUtils.hasUserId(USER_SEC_KILL + key, userId)) {
                log.info("该用户已经秒杀成功,商品ID:{},用户ID:{}", key, userId);
                return SecKillInventoryEnum.ALREADY_SEC_KILL_SUCCESS.code();
            }
            //判断缓存商品是否存在、库存不足
            String cacheStock = isCacheStock(key, num);
            if (!SecKillInventoryEnum.SEC_KILL_SUCCESS.code().equals(cacheStock)) {
                return cacheStock;
            }
            //减少库存
            redisUtils.decrement(key, num);
            //保存用户到redis
            redisUtils.setKey(USER_SEC_KILL + key, userId);
            code = SecKillInventoryEnum.SEC_KILL_SUCCESS.code();
        } catch (Exception e) {
            log.error("秒杀商品减少库存失败，商品id：{}，用户id:{}", key, userId, e);
            code = SecKillInventoryEnum.SEC_KILL_ERROR.code();
        } finally {
            lock.unlock();
        }
        return code;
    }

    /**
     * 判断缓存库存是否存在、不足
     *
     * @param key
     * @param num
     * @return
     */
    public String isCacheStock(String key, int num) {
        if (!redisUtils.hasKey(key)) {
            log.info("缓存中库存不存在,商品id：{}", key);
            return SecKillInventoryEnum.NOT_EXIST.code();
        }
        int stock = redisUtils.getStock(key);
        if (stock == -1 || stock == 0 || stock < num) {
            log.info("缓存中库存不足,商品id：{}", key);
            return SecKillInventoryEnum.INVENTORY_SHORTAGE.code();
        }
        return SecKillInventoryEnum.SEC_KILL_SUCCESS.code();
    }

    /**
     * 新增redis缓存库存(还原库存)
     *
     * @param key 库存key
     * @param num 库存数量
     * @return
     */
    public String addStock(String key, Integer num) {
        return addStock(key, 2L, num);
    }

    /**
     * 加redis缓存库存
     *
     * @param key    库存key
     * @param expire 过期时间
     * @param num    库存数量
     * @return
     */
    public String addStock(String key, Long expire, Integer num) {
        String state = null;
        Assert.notNull(expire, "初始化库存失败，库存过期时间不能为null");
        //获取redis分布事务锁
        RLock lock = redisson.getLock(LOCK_STOCK + key);
        try {
            lock.lock();
            boolean hasKey = redisUtils.hasKey(key);
            // 判断key是否存在，存在就直接更新
            if (hasKey) {
                //增量更新数据
                redisUtils.increment(key, num);
            } else {
                // 初始化库存
                redisUtils.set(key, num, expire, TimeUnit.HOURS);
            }
            state = SecKillInventoryEnum.SEC_KILL_SUCCESS.code();
        } catch (Exception e) {
            log.error("添加数据失败，商户id:{},错误信息：{}", key, e.getMessage(), e);
            state = SecKillInventoryEnum.SEC_KILL_ERROR.code();
        } finally {
            lock.unlock();
        }
        return state;
    }

    /**
     * 获取redis缓存库存
     *
     * @param key
     * @return
     */
    public int getStock(String key) {
        return redisUtils.getStock(key);
    }

}